package top.yoahh.aspect;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import top.yoahh.annotation.AutoLog;
import top.yoahh.system.entity.SysLog;
import top.yoahh.system.service.SysLogService;
import top.yoahh.utils.IPUtil;
import top.yoahh.utils.SpringContextUtil;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Aspect
@Slf4j
@Component
public class AutoLogAspect {

    @Autowired
    private SysLogService sysLogService;


    @Pointcut("@annotation(top.yoahh.annotation.AutoLog)")
    public void executeService() {
    }

    @Around("executeService()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        // 开始时间
        long beginTime = System.currentTimeMillis();
        // 执行方法
        Object result = joinPoint.proceed();
        // 执行时长
        long time = System.currentTimeMillis() - beginTime;
        // 保存日志
        saveLog(joinPoint, time, result);
        // 返回结果
        return result;

    }

    private void saveLog(ProceedingJoinPoint joinPoint, long time, Object obj) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取注解
        AutoLog annotation = method.getAnnotation(AutoLog.class);

        // 接口地址
        String className = joinPoint.getTarget().getClass().getName();
        // 接口方法
        String methodName = signature.getName();

        // 获取request
        HttpServletRequest httpServletRequest = SpringContextUtil.getHttpServletRequest();
        // 请求参数
        String requestParams = getRequestParams(httpServletRequest, joinPoint);
        StringBuffer requestURL = httpServletRequest.getRequestURL();
        // 请求IP
        String ip = IPUtil.getIpAddress(httpServletRequest);
        SysLog Syslog = new SysLog();
        Syslog.setCostTime(time);
        Syslog.setIp(ip);
        Syslog.setRequestParam(requestParams);
        Syslog.setMethod(className + "." + methodName + "()");
        Syslog.setRequestUrl(requestURL.toString());
        Syslog.setLogContent(annotation.value());
        Syslog.setLogType(annotation.logType());
        Syslog.setRequestType(httpServletRequest.getMethod());
        log.info("=================>开始：记录日志<=================");
        log.info("方法名称：{}", annotation.value());
        log.info("请求地址：{}", requestURL);
        log.info("请求接口：{}", className + "." + methodName + "()");
        log.info("请求I P：{}", ip);
        log.info("请求参数：{}", requestParams);
        log.info("耗   时：{}ms", time);
        log.info("=================>结束：记录日志<=================");
        sysLogService.save(Syslog);
    }

    /**
     * 获取请求参数
     *
     * @param request   HttpServletRequest对象
     * @param joinPoint JoinPoint对象
     * @return 参数
     */
    private String getRequestParams(HttpServletRequest request, JoinPoint joinPoint) {
        String httpMethod = request.getMethod();
        String params = "";
        if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
            Object[] paramsArray = joinPoint.getArgs();
            Object[] arguments = new Object[paramsArray.length];
            for (int i = 0; i < paramsArray.length; i++) {
                // ServletRequest、ServletResponse、MultipartFile不能序列化，所以从入参排除，否则报错
                if (paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile)
                    continue;
                arguments[i] = paramsArray[i];
            }
            // 排除日志过长的直接过滤
            PropertyFilter propertyFilter = new PropertyFilter() {
                @Override
                public boolean apply(Object o, String name, Object value) {
                    if (value != null && value.toString().length() > 500)
                        return false;
                    return true;
                }
            };
            params = JSONObject.toJSONString(arguments, propertyFilter);
        } else {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Object[] args = joinPoint.getArgs();
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] parameterNames = u.getParameterNames(method);
            if (args != null && parameterNames != null) {
                for (int i = 0; i < args.length; i++) {
                    params += " " + parameterNames[i] + ": " + args[i];
                }
            }
        }
        return params;
    }
}
